#include "wpmenubar.h"

#include <QApplication>
#include <QStylePainter>
#include <QSizePolicy>
#include <QHBoxLayout>
#include <QVBoxLayout>
#include <QAction>

/*
 * WPMenuButton
 */    
WPMenuButton::WPMenuButton (QWidget *parent) :
    QAbstractButton(parent),
    iOrientation(Qt::Horizontal),
    iAction(NULL)
{
    connect(this, SIGNAL(clicked()), this, SLOT(thisClicked()));
    setFocusPolicy(Qt::NoFocus);
}

WPMenuButton::WPMenuButton (const QString &text, Qt::Orientation orientation, QWidget *parent) :
    QAbstractButton(parent),
    iOrientation(orientation),
    iAction(NULL)
{
    if (!text.isEmpty())
        setText(text);

    connect(this, SIGNAL(clicked()), this, SLOT(thisClicked()));
}

void WPMenuButton::setOrientation (Qt::Orientation orientation)
{
    iOrientation = orientation;

    // size policy
    switch (orientation) {
        case Qt::Horizontal:
            setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);

            break;

        case Qt::Vertical:
            setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding);

            break;
    }

    // redraw and relayout
    update();
    updateGeometry();
}
    
void WPMenuButton::setAction (QAction *action)
{
    iAction = action;

    // use text/icon
    if(iAction)
        {
        setText(iAction->text());
        setIcon(iAction->icon());
        }
}
    
QSize WPMenuButton::sizeHint () const
{
    int w = 0, h = 0;

    // style options
    QStyleOptionButton option;
    initStyleOption(&option);

    // icon
    if (!icon().isNull()) {
        w += option.iconSize.width();
        h = qMax(h, option.iconSize.height());
    }

    // text
    if (!text().isNull()) {
        QFontMetrics fm = fontMetrics();

        QSize fz = fm.size(Qt::TextSingleLine, text());

        w += fz.width();
        h = qMax(h, fz.height());
    }

    // translate?
    if (iOrientation == Qt::Vertical) {
        int tmp;
     
        // transpose   
        tmp = w;
        w = h;
        h = tmp;
    }
    
    // update rect
    option.rect.setSize(QSize(w, h));

    // calc
    QSize sizeHint = style()->sizeFromContents(QStyle::CT_PushButton, &option, QSize(w, h), this);
    
    // expand to minimum size
    sizeHint = sizeHint.expandedTo(QApplication::globalStrut());

    // XXX: cache
    return sizeHint;
}
    
QSize WPMenuButton::minimumSizeHint () const
{
    return sizeHint();
}
    
void WPMenuButton::initStyleOption (QStyleOptionButton *option) const
{
    // init
    option->initFrom(this);
    option->features = QStyleOptionButton::Flat;

    // features
    if (isDown())
        option->state |= QStyle::State_Sunken;

    else
        option->state |= QStyle::State_Raised;

    // elements
    option->text = text();
    option->icon = icon();
    option->iconSize = iconSize();

    if (iOrientation == Qt::Vertical)
        // transpose
        option->rect.setSize(QSize(option->rect.height(), option->rect.width()));
}
   
void WPMenuButton::paintEvent (QPaintEvent *e)
{
    (void) e;

    QStylePainter painter (this);
    
    // style options
    QStyleOptionButton option;
    initStyleOption(&option);

    // translate?
    if (iOrientation == Qt::Vertical) {
        // translate origin to bottom-left (i.e. bottom-right edge once rotated)
        painter.translate(QPoint(0, size().height()));

        // rotate left
        painter.rotate(-90);
    }

    // draw ourselves, translated...
    painter.drawControl(QStyle::CE_PushButton, option);
}
    
void WPMenuButton::thisClicked ()
{
    if (iAction)
        iAction->trigger();
}

/*
 * WPMenu
 */
WPMenu::WPMenu (QWidget *parent) :
    QFrame(parent),
    iCurrentOrientation(-1), // invalid
    iActiveButtons()
{

    // text/icon comes from the actions set
    iOptionsButton = new WPMenuButton(this);
    iSaveButton = new WPMenuButton(this);
    iCancelButton = new WPMenuButton(this);
    iBackButton = new WPMenuButton(this);
    iExitButton = new WPMenuButton(this);
    iSelectButton = new WPMenuButton(this);

    // XXX: why does these leak through?
    iBackButton->hide();
    iCancelButton->hide();
    iSaveButton->hide();
    iOptionsButton->hide();
    iExitButton->hide();
    iSelectButton->hide();

    // popup menu
    iPopupMenu = new PopupMenu(this);
    connect(iPopupMenu, SIGNAL(aboutToHide()), this, SLOT(menuHidden()));

    iMenuAction = new QAction(tr("Options"), this);
    iMenuAction->setSoftKeyRole(QAction::PositiveSoftKey);
    
    iOptionsButton->setAction(iMenuAction);
    connect(iMenuAction, SIGNAL(triggered()), this, SLOT(showPopupMenu()));
    
    iSelectFromMenuAction = new QAction(tr("Select"), this);
    connect(iSelectFromMenuAction, SIGNAL(triggered()), this, SLOT(triggerCurrentMenuItem()));
    iMenuAction->setSoftKeyRole(QAction::PositiveSoftKey);
    
    iHideMenuAction = new QAction(tr("Cancel"), this);
    connect(iHideMenuAction, SIGNAL(triggered()), iPopupMenu, SLOT(hide()));
    iMenuAction->setSoftKeyRole(QAction::NegativeSoftKey);
    
    iExitAction = new QAction(tr("Exit"), this);
    iExitAction->setSoftKeyRole(QAction::NegativeSoftKey);
    iExitButton->setAction(iExitAction);
    connect(iExitAction, SIGNAL(triggered()), this, SIGNAL(exitApplication()));

    /** XXX: default orientation */
    // setOrientation(Qt::Vertical);
    
    setFocusPolicy(Qt::NoFocus);
}

void WPMenu::setOrientation (Qt::Orientation orientation)
{
    if (orientation == iCurrentOrientation)
        // nothing
        return;

    // update
    iCurrentOrientation = orientation;

    iOptionsButton->setOrientation(orientation);
    iSaveButton->setOrientation(orientation);
    iCancelButton->setOrientation(orientation);
    iBackButton->setOrientation(orientation);
    iExitButton->setOrientation(orientation);
    iSelectButton->setOrientation(orientation);
    
    // current buttons, to restore    
    QList<WPMenuButton *> activeButtons = iActiveButtons;
    
    // current layout
    QLayout *layout = this->layout();

    // clear and remove old layout
    if (layout) {
        setActiveButtons(QList<WPMenuButton *> ());

        delete layout;
    }

    // new layout
    if (orientation == Qt::Horizontal) {
        layout = new QHBoxLayout();

        setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);

    } else /* Qt::Vertical */ {
        layout = new QVBoxLayout();
        qobject_cast<QBoxLayout *>(layout)->setDirection(QBoxLayout::BottomToTop);
        setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding);
    }

    setLayout(layout);
 
    // restore buttons
    setActiveButtons(activeButtons);
       
    // XXX: inherit from parent somewhere?
    layout->setSpacing(0);
    layout->setContentsMargins(0, 0, 0, 0);
    
    // fix popup menu position
    if (iPopupMenu->isVisible())
        iPopupMenu->move(getPopupMenuPosition());
}
    
QAction * WPMenu::addMenuItem (QAction *action)
{
    iPopupMenu->addAction(action);

    return action;
}

bool WPMenu::ActivateSoftkeyAction(Softkey softkey)
{
    switch(iActiveButtons.count())
        {
        case 3:
            if(softkey == NegativeSoftkey)
                {
                iActiveButtons[2]->click();
                return true;
                }
            else if(softkey == SelectSoftkey)
                {
                iActiveButtons[1]->click();
                return true;
                }
            // fall through
        case 2:
            if(softkey == NegativeSoftkey)
                {
                iActiveButtons[1]->click();
                return true;
                }
            // fall through
        case 1:
            if(softkey == PositiveSoftkey)
                {
                iActiveButtons[0]->click();
                return true;
                }
            return false;
        default:
            return false;
        }
}
    
void WPMenu::setActiveButtons (const QList<WPMenuButton *> &activeButtons)
{
    QLayout *layout = this->layout();

    Q_ASSERT(layout);

    // remove old items
    for (QListIterator<WPMenuButton *> i(iActiveButtons); i.hasNext(); ) {
        WPMenuButton *button = i.next();

        button->hide();
        layout->removeWidget(button);
    }

    // set new list
    iActiveButtons = activeButtons;

    // add items
    for (QListIterator<WPMenuButton *> i(iActiveButtons); i.hasNext(); ) {
        WPMenuButton *button = i.next();

        layout->addWidget(button);
        button->show();
    }
}
    
void WPMenu::showMenuQuit ()
{
    QList<WPMenuButton *> activeButtons;
    activeButtons << iOptionsButton << iExitButton;

    setActiveButtons(activeButtons);
}

void WPMenu::showSaveCancel (QAction *save, QAction *cancel)
{
    QList<WPMenuButton *> activeButtons;
    activeButtons << iSaveButton << iCancelButton;

    iSaveButton->setAction(save);
    iCancelButton->setAction(cancel);

    setActiveButtons(activeButtons);
}

void WPMenu::showSaveExit (QAction *save)
{
    QList<WPMenuButton *> activeButtons;
    activeButtons << iSaveButton << iExitButton;

    iSaveButton->setAction(save);

    setActiveButtons(activeButtons);
}
    
void WPMenu::showBack (QAction *back)
{
    QList<WPMenuButton *> activeButtons;
    activeButtons << iOptionsButton << iBackButton;

    iBackButton->setAction(back);

    setActiveButtons(activeButtons);
}

QPoint WPMenu::getPopupMenuPosition ()
{
    // show next to the button; works for both portrait and landscape
    QPoint pos = iOptionsButton->mapToGlobal(QPoint(0, 0));
    QSize size = iPopupMenu->sizeHint();
   
    // offset a little to keep the button area clear
    if (iCurrentOrientation == Qt::Vertical)
        pos.rx() -= size.width();

    else
        pos.ry() -= size.height();

    return pos;
}

void WPMenu::showPopupMenu ()
{
    // display
    iPopupMenu->setWindowOpacity(0.9);
    iPopupMenu->popup(getPopupMenuPosition());  // XXX: use show + setGeometry instead?
 
    // store old buttons
    iActiveButtonsBeforeMenu = iActiveButtons;
    
    // Set buttons
    QList<WPMenuButton *> activeButtons;
    activeButtons << iSelectButton << iCancelButton;
    iSelectButton->setAction(iSelectFromMenuAction);
    iCancelButton->setAction(iHideMenuAction);
    setActiveButtons(activeButtons);
    
    // set focus
    iPopupMenu->setFocus();
}
    
void WPMenu::moveEvent (QMoveEvent *event)
{
    QFrame::moveEvent(event);

    // XXX: buggy since we might still have our old iCurrentOrientation here
    if (iPopupMenu->isVisible())
        iPopupMenu->move(getPopupMenuPosition());
}

void WPMenu::triggerCurrentMenuItem()
{
    iPopupMenu->activeAction()->trigger();
    iPopupMenu->hide();
}

void WPMenu::menuHidden()
{
    setActiveButtons(iActiveButtonsBeforeMenu);
}
